package storage

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/tencentyun/cos-go-sdk-v5"
)

// COSConfig 腾讯云COS配置
type COSConfig struct {
	SecretID    string // 密钥ID
	SecretKey   string // 密钥
	Region      string // 地域
	Bucket      string // 存储桶名称
	BaseURL     string // 自定义域名（可选）
	PathPrefix  string // 路径前缀（可选）
	IsPublic    bool   // 是否公开访问
	Timeout     int    // 超时时间（秒）
	MaxFileSize int64  // 最大文件大小（字节）
}

// COSStorage 腾讯云COS存储实现
type COSStorage struct {
	client     *cos.Client
	config     *COSConfig
	bucketURL  string
	serviceURL string
}

// NewCOSStorage 创建腾讯云COS存储
func NewCOSStorage(config *COSConfig) (Storage, error) {
	if config == nil {
		return nil, errors.New("COS配置不能为空")
	}

	if config.SecretID == "" || config.SecretKey == "" {
		return nil, errors.New("COS密钥ID和密钥不能为空")
	}

	if config.Bucket == "" {
		return nil, errors.New("COS存储桶名称不能为空")
	}

	if config.Region == "" {
		return nil, errors.New("COS地域不能为空")
	}

	// 构建存储桶URL
	bucketURL := fmt.Sprintf("https://%s.cos.%s.myqcloud.com", config.Bucket, config.Region)

	// 如果提供了自定义域名，则使用自定义域名
	if config.BaseURL != "" {
		if !strings.HasPrefix(config.BaseURL, "http://") && !strings.HasPrefix(config.BaseURL, "https://") {
			config.BaseURL = "https://" + config.BaseURL
		}
		bucketURL = config.BaseURL
	}

	// 解析存储桶URL
	u, err := url.Parse(bucketURL)
	if err != nil {
		return nil, fmt.Errorf("解析存储桶URL失败: %w", err)
	}

	// 解析服务URL
	su, err := url.Parse(fmt.Sprintf("https://cos.%s.myqcloud.com", config.Region))
	if err != nil {
		return nil, fmt.Errorf("解析服务URL失败: %w", err)
	}

	// 创建COS客户端
	client := cos.NewClient(
		&cos.BaseURL{BucketURL: u, ServiceURL: su},
		&http.Client{
			Transport: &cos.AuthorizationTransport{
				SecretID:  config.SecretID,
				SecretKey: config.SecretKey,
			},
			Timeout: time.Duration(config.Timeout) * time.Second,
		},
	)

	return &COSStorage{
		client:     client,
		config:     config,
		bucketURL:  bucketURL,
		serviceURL: su.String(),
	}, nil
}

// Upload 上传文件
func (s *COSStorage) Upload(ctx context.Context, name string, reader io.Reader, option *UploadOption) (*FileInfo, error) {
	// 如果有路径前缀，则添加到文件路径前
	filePath := name
	if s.config.PathPrefix != "" {
		filePath = filepath.Join(s.config.PathPrefix, filePath)
	}

	// 规范化文件路径，使用正斜杠
	filePath = strings.ReplaceAll(filePath, "\\", "/")

	// 设置对象ACL
	var acl string
	if option != nil && option.IsPublic || s.config.IsPublic {
		acl = "public-read"
	} else {
		acl = "private"
	}

	// 设置上传选项
	opt := &cos.ObjectPutOptions{
		ACLHeaderOptions: &cos.ACLHeaderOptions{
			XCosACL: acl,
		},
		ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{},
	}

	// 设置内容类型
	if option != nil && option.ContentType != "" {
		opt.ContentType = option.ContentType
	}

	// 上传文件
	_, err := s.client.Object.Put(ctx, filePath, reader, opt)
	if err != nil {
		return nil, fmt.Errorf("上传文件到COS失败: %w", err)
	}

	// 获取文件URL
	url, err := s.GetURL(ctx, filePath, 0)
	if err != nil {
		return nil, err
	}

	// 返回文件信息
	return &FileInfo{
		Name:         filepath.Base(name),
		Path:         filePath,
		URL:          url,
		Size:         0, // 无法获取准确大小
		ContentType:  option.ContentType,
		LastModified: time.Now(),
	}, nil
}

// Delete 删除文件
func (s *COSStorage) Delete(ctx context.Context, path string) error {
	// 如果有路径前缀，则添加到文件路径前
	filePath := path
	if s.config.PathPrefix != "" {
		filePath = filepath.Join(s.config.PathPrefix, filePath)
	}

	// 规范化文件路径，使用正斜杠
	filePath = strings.ReplaceAll(filePath, "\\", "/")

	// 删除文件
	_, err := s.client.Object.Delete(ctx, filePath)
	if err != nil {
		return fmt.Errorf("删除COS文件失败: %w", err)
	}

	return nil
}

// GetURL 获取文件URL
func (s *COSStorage) GetURL(ctx context.Context, path string, expiration time.Duration) (string, error) {
	// 如果有路径前缀，则添加到文件路径前
	filePath := path
	if s.config.PathPrefix != "" && !strings.HasPrefix(filePath, s.config.PathPrefix) {
		filePath = filepath.Join(s.config.PathPrefix, filePath)
	}

	// 规范化文件路径，使用正斜杠
	filePath = strings.ReplaceAll(filePath, "\\", "/")

	// 如果是公开访问，则直接返回URL
	if s.config.IsPublic {
		return fmt.Sprintf("%s/%s", strings.TrimRight(s.bucketURL, "/"), strings.TrimLeft(filePath, "/")), nil
	}

	// 如果是私有访问，则生成带签名的URL
	if expiration <= 0 {
		expiration = time.Hour // 默认1小时
	}

	presignedURL, err := s.client.Object.GetPresignedURL(ctx, http.MethodGet, filePath, s.config.SecretID, s.config.SecretKey, expiration, nil)
	if err != nil {
		return "", fmt.Errorf("生成COS签名URL失败: %w", err)
	}

	return presignedURL.String(), nil
}

// GetFileInfo 获取文件信息
func (s *COSStorage) GetFileInfo(ctx context.Context, path string) (*FileInfo, error) {
	// 如果有路径前缀，则添加到文件路径前
	filePath := path
	if s.config.PathPrefix != "" {
		filePath = filepath.Join(s.config.PathPrefix, filePath)
	}

	// 规范化文件路径，使用正斜杠
	filePath = strings.ReplaceAll(filePath, "\\", "/")

	// 获取文件信息
	resp, err := s.client.Object.Head(ctx, filePath, nil)
	if err != nil {
		return nil, fmt.Errorf("获取COS文件信息失败: %w", err)
	}

	// 获取文件URL
	url, err := s.GetURL(ctx, filePath, 0)
	if err != nil {
		return nil, err
	}

	// 解析最后修改时间
	lastModified := time.Now()
	if lastModifiedStr := resp.Header.Get("Last-Modified"); lastModifiedStr != "" {
		if t, err := time.Parse(time.RFC1123, lastModifiedStr); err == nil {
			lastModified = t
		}
	}

	// 获取文件大小
	contentLength := int64(0)
	if contentLengthStr := resp.Header.Get("Content-Length"); contentLengthStr != "" {
		if size, err := strconv.ParseInt(contentLengthStr, 10, 64); err == nil {
			contentLength = size
		}
	}

	// 返回文件信息
	return &FileInfo{
		Name:         filepath.Base(path),
		Path:         filePath,
		URL:          url,
		Size:         contentLength,
		ContentType:  resp.Header.Get("Content-Type"),
		LastModified: lastModified,
	}, nil
}

// ListFiles 列出指定目录下的文件
func (s *COSStorage) ListFiles(ctx context.Context, prefix string) ([]*FileInfo, error) {
	// 如果有路径前缀，则添加到文件路径前
	if s.config.PathPrefix != "" {
		prefix = filepath.Join(s.config.PathPrefix, prefix)
	}

	// 规范化文件路径，使用正斜杠
	prefix = strings.ReplaceAll(prefix, "\\", "/")

	// 列出文件
	opt := &cos.BucketGetOptions{
		Prefix:    prefix,
		MaxKeys:   1000,
		Delimiter: "",
	}

	result, _, err := s.client.Bucket.Get(ctx, opt)
	if err != nil {
		return nil, fmt.Errorf("列出COS文件失败: %w", err)
	}

	// 处理结果
	files := make([]*FileInfo, 0, len(result.Contents))
	for _, object := range result.Contents {
		// 跳过目录
		if strings.HasSuffix(object.Key, "/") {
			continue
		}

		// 获取文件URL
		url, err := s.GetURL(ctx, object.Key, 0)
		if err != nil {
			continue
		}

		// 解析最后修改时间
		lastModified := time.Now()
		if t, err := time.Parse(time.RFC3339, object.LastModified); err == nil {
			lastModified = t
		}

		// 添加文件信息
		files = append(files, &FileInfo{
			Name:         filepath.Base(object.Key),
			Path:         object.Key,
			URL:          url,
			Size:         object.Size,
			ContentType:  "", // COS不返回内容类型
			LastModified: lastModified,
		})
	}

	return files, nil
}

// Type 获取存储类型
func (s *COSStorage) Type() StorageType {
	return StorageTypeCOS
}
